﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Timers;

namespace Asomy.Model
{
    class LayerHandler
    {
        public static List<Layer> m_layers = new List<Layer>();        
        public static List<LayerChange> m_layerChanges = new List<LayerChange>();        
        public static bool m_needUpdate = false;
        public static int m_activeLayer;
        public byte[] m_image;

        public int m_width, m_height;        

        public static int ActiveLayerID
        {
            get { return m_activeLayer; }
            set
            {
                if (value < 0)
                {
                    m_activeLayer = 0;
                }
                else if (value >= m_layers.Count)
                {
                    m_activeLayer = m_layers.Count - 1;
                }
                else
                {
                    m_activeLayer = value;
                }
            }
        }

        public LayerHandler(int a_width,int a_height)
        {
            m_layers.Clear();

            m_width = a_width;
            m_height = a_height;

            AddBackgroundLayer();

            AddLayer("Layer 1");

            ActiveLayerID = 1;

            m_image = new byte[m_width * m_height * 4];

            CreateImage();

            
        }       

        //Used when opening an already existing image
        /// <summary>
        /// 
        /// </summary>
        /// <param name="a_width"></param>
        /// <param name="a_height"></param>
        /// <param name="a_colorMatrix"></param>
        public LayerHandler(int a_width, int a_height,Color[,] a_colorMatrix)
        {
            m_layers.Clear();
            m_layerChanges.Clear();
            

            m_width = a_width;
            m_height = a_height;

            AddBackgroundLayer();

            m_image = new byte[m_width * m_height * 4];

            AddLayer("Layer1");

            ActiveLayerID = 1;

            m_layers[1].m_pixels = a_colorMatrix;

            CreateImage();

            m_needUpdate = true;

        }        

        private void AddBackgroundLayer()
        {
            m_layers.Add(new Layer(m_width, m_height));
            m_layers[m_layers.Count - 1].m_id = m_layers.Count - 1;
        }

        public void AddLayer(string a_name)
        {
            Layer f_layer = new Layer(m_width, m_height, a_name);

            m_layers.Add(f_layer);

            m_layers[m_layers.Count - 1].m_id = m_layers.Count - 1;

            ActiveLayerID = m_layers.Count - 1;
        }       

        #region LayersListBox_Functions
        
        /// <summary>
        /// Paints on the layer. Changes the pixels colors!  A.t.m. only has 1 color but can fix a List of a class with positions and colors.
        /// </summary>
        /// <param name="a_id">The layerID that is getting pixels edited</param>
        /// <param name="a_positions">A list of all poisitions that are to be updated</param>
        /// <param name="a_color">The color that pixel is getting</param>
        public static void PaintOnLayer(int a_id, List<int[]> a_positions, Color a_color)
        {           
                try
                {
                    for (int i = 0; i < a_positions.Count; i++)
                    {
                        if (m_layers[a_id].ChangePixelColor(a_positions[i][0], a_positions[i][1], a_color))
                        {
                            LayerChange f_layerchange = new LayerChange(a_id, a_positions[i][0], a_positions[i][1]);
                            if (f_layerchange != null)
                            {
                                m_layerChanges.Add(f_layerchange);
                            }
                        }
                    }                
                }
                catch
                {
                    m_needUpdate = true;
                }                         
        }        

        /// <summary>
        /// Attempts to rename the Layer, returns true of successful
        /// </summary>        
        public static bool RenameLayer(int a_id, string a_newName)
        {
            try
            {
                if (m_layers[a_id].m_id == a_id && m_layers[a_id].m_name != a_newName)
                {                    
                    return true;
                }
                return false;
            }
            catch (Exception e)
            {                
                m_needUpdate = true;
                return false;
            }
        }

        /// <summary>
        /// Attempts to delete a layer. It checks if m_layers.ID is the same as parameter a_id
        /// </summary>        
        public static void DeleteLayer(int a_id)
        {
            try
            {
                if (m_layers.Count > 2)
                {
                    m_needUpdate = true;
                    if (m_layers[a_id].m_id == a_id)
                    {
                        m_layers.RemoveAt(a_id);
                        UpdateLayers();
                        if (a_id <= ActiveLayerID)
                        {
                            ActiveLayerID--;
                        }
                    }
                }
            }
            catch
            {
                
            }
        }

        /// <summary>
        /// Updates layers with new ID
        /// </summary>
        public static void UpdateLayers()
        {          
            for (int i = 0; i < m_layers.Count;i++)
            {
                m_layers[i].m_id = i;
            }
        }

        /// <summary>
        /// Sets layers visibility to Visible
        /// </summary>
        /// <param name="a_id"></param>
        public static void ShowLayer(int a_id)
        {
            try
            {
                m_needUpdate = true;
                if (m_layers[a_id].m_id == a_id)
                {
                    m_layers[a_id].Show();
                    
                }
            }
            catch
            {
                
            }
        }

        /// <summary>
        /// Sets layers Visibility to Hidden
        /// </summary>        
        public static void HideLayer(int a_id)
        {
            try
            {
                m_needUpdate = true;
                if (m_layers[a_id].m_id == a_id)
                {
                    m_layers[a_id].Hide();                    
                }
            }
            catch
            {
                
            }
        }

        /// <summary>
        /// Locks the layer!
        /// </summary>        
        public static void LockLayer(int a_id)
        {
            try
            {                
                if (m_layers[a_id].m_id == a_id)
                {
                    m_layers[a_id].Lock();
                }
            }
            catch
            {
                m_needUpdate = true;
            }
        }

        /// <summary>
        /// Unlocks Layer
        /// </summary>        
        public static void UnLockLayer(int a_id)
        {
            try
            {
                if (m_layers[a_id].m_id == a_id)
                {
                    m_layers[a_id].UnLock();
                }
            }
            catch
            {
                m_needUpdate = true;
            }
        }
        #endregion

        #region ImageFunctions

        /// <summary>
        /// Redraws whole image from all layers. Should only be used at start of program and when a layer changes visibility.
        /// </summary>
        public void CreateImage()
        {
            Color[,] f_imageColor = new Color[m_width, m_height];

            /*for (int i = 0; i < m_layers.Count; i++)
            {
                if (m_layers[i].Visible)
                {
                   if (m_layers[i].m_pixels[x, y].A > 0)
                    {
                        f_imageColor[x, y] = m_layers[i].m_pixels[x, y];
                    }
                    else if (f_imageColor[x, y] == null)
                    {
                        f_imageColor[x, y] = new Color();
                    }
                }
            }*/

            for (int x = 0; x < m_width; x++)
            {
                for (int y = 0; y < m_height; y++)
                {
                    f_imageColor[x, y] = CalcColor(x, y);
                }
            }
            
            int ofs = 0;
            for (int y = 0; y < m_height; y++)
            {
                for (int x = 0; x < m_width; x++)
                {
                    //is in this order because bitmap source needs it in that order, otherwise it gets MESSED up
                    m_image[ofs++] = f_imageColor[x, y].B;
                    m_image[ofs++] = f_imageColor[x, y].G;
                    m_image[ofs++] = f_imageColor[x, y].R;
                    m_image[ofs++] = f_imageColor[x, y].A;
                }
            }               
        }

        /// <summary>
        /// Updates the image array with the changes made from any paint function.
        /// </summary>
        public void UpdateImage()
        {
            try
            {
                for (int i = 0; i < m_layerChanges.Count; )
                {
                    try
                    {
                        int x = m_layerChanges[i].m_x;
                        int y = m_layerChanges[i].m_y;

                        int pos = (m_width * y + x) * 4;

                        Color f_imageColor = CalcColor(x, y);

                        /*for (int j = m_layers.Count - 1; j >= 0; j--)
                        //{
                            if (m_layers[j].Visible)
                            {
                                if (m_layers[j].m_pixels[x, y].A > 0)
                                {
                                    f_imageColor = m_layers[j].m_pixels[x, y];
                                    break;
                                }
                            }
                        //}*/

                        m_image[pos++] = f_imageColor.B;
                        m_image[pos++] = f_imageColor.G;
                        m_image[pos++] = f_imageColor.R;
                        m_image[pos] = f_imageColor.A;
                    }
                    catch (Exception e)
                    {
                        throw new Exception(e + "lolol");
                    }
                    m_layerChanges.RemoveAt(i);
                }
                
            }
            catch (Exception e)
            {                
            }
        }

        /// <summary>
        /// Returns the image. But before that it updates the pixels first in it.
        /// </summary>        
        public byte[] GetImage()
        {    
            
            UpdateImage();
            
            return m_image;            
        }

        public byte[] GetFinalImage()
        {
            Color[,] f_imageColor = new Color[m_width, m_height];            

            //for (int i = 1; i < m_layers.Count; i++)
            //{                
                for (int x = 0; x < m_width; x++)
                {                   
                    for (int y = 0; y < m_height; y++)
                    {
                        f_imageColor[x, y] = CalcFinalColor(x, y);
                       /* if (m_layers[i].m_pixels[x, y].A > 0)
                        {
                            f_imageColor[x, y] = m_layers[i].m_pixels[x, y];
                        }
                        else if (f_imageColor[x, y] == null)
                        {
                            f_imageColor[x, y] = new Color();
                        }*/
                    }
                }
                
            //}           
           
            int ofs = 0;

            byte[] f_image = new byte[m_width * m_height * 4];

            for (int y = 0; y < m_height; y++)
            {
                for (int x = 0; x < m_width; x++)
                {
                    //is in this order because bitmap source needs it in that order, otherwise it gets MESSED up
                    f_image[ofs++] = f_imageColor[x, y].B;
                    f_image[ofs++] = f_imageColor[x, y].G;
                    f_image[ofs++] = f_imageColor[x, y].R;
                    f_image[ofs++] = f_imageColor[x, y].A;
                }
            }

            return f_image;
        }

        private Color CalcColor(int a_x, int a_y)
        {
            Color f_color = new Color();
            for (int i = 0; i < m_layers.Count; i++)
            {
                if (m_layers[i].Visible)
                {
                    float f_alpha = m_layers[i].m_pixels[a_x, a_y].ScA;
                    f_color = Color.Add(Color.Multiply(f_color, 1 - f_alpha), Color.Multiply(m_layers[i].m_pixels[a_x, a_y], f_alpha));
                }
            }

            return f_color;
        }

        /// <summary>
        /// The Final color used when saving image
        /// </summary>      
        private Color CalcFinalColor(int a_x, int a_y)
        {
            Color f_color = new Color();
            for (int i = 1; i < m_layers.Count; i++)
            {
                float f_alpha = m_layers[i].m_pixels[a_x, a_y].ScA;
                f_color = Color.Add(Color.Multiply(f_color, 1 - f_alpha), Color.Multiply(m_layers[i].m_pixels[a_x, a_y], f_alpha));
            }

            return f_color;
        }

        #endregion
    }
}
